package com.seres.util;

import com.seres.constant.CommonKey;
import com.seres.vo.AuthUser;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Clock;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.impl.DefaultClock;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;

@Component
public class TokenTools implements Serializable {

    private static final long serialVersionUID = 7557436598093733852L;

    private final Clock clock = DefaultClock.INSTANCE;


    @Value("${jwt.secret:}")
    private String secret;

    @Value("${jwt.expiration:}")
    private Long expiration;


    private static final String USER_ID = "user_id";
    private static final String ONLY_ONE = "only_one";
    private static final String ROLES = "roles";


    public AuthUser authUser(String token){
        Claims claims = getAllClaimsFromToken(token);
        AuthUser user = new AuthUser();
//        Object role = claims.get(ROLES);
//        List<Long> roles = new ArrayList<>();
//        if (role instanceof ArrayList<?>) {
//            for (Object o : (List<?>) role) {
//                roles.add((Long) o);
//            }
//        }
        user.setUserId(NumberUtils.toLong("" + claims.get(USER_ID)));
        user.setOnlyOne(BooleanUtils.toBoolean(""+claims.get(ONLY_ONE)));
//        user.setRoles(roles);
        return user;
    }

    public Date getIssuedAtDateFromToken(String token) {
        return getClaimFromToken(token, Claims::getIssuedAt);
    }

    public Date getExpirationDateFromToken(String token) {
        return getClaimFromToken(token, Claims::getExpiration);
    }

    public <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) {
        final Claims claims = getAllClaimsFromToken(token);
        return claimsResolver.apply(claims);
    }

    private Claims getAllClaimsFromToken(String token) {
        return Jwts.parser()
                .setSigningKey(secret)
                .parseClaimsJws(token)
                .getBody();
    }

    /**
     * @author Tan-Bowen
     * @description TODO 生成token
     * @date 2021/8/3 11:03
     * @param [userid, roles, onlyOne]
     * @paramType [long, java.util.List<java.lang.Long>, boolean]
     * @return java.lang.String
     */
    public String generateToken(long userid, boolean onlyOne) {
        Map<String, Object> claims = new HashMap<>();
        claims.put(USER_ID, userid);
        claims.put(ONLY_ONE, onlyOne);
//        claims.put(ROLES,roles);
        return StringUtils.append(CommonKey.BEARER,doGenerateToken(claims, userid + ""));
    }

    private String doGenerateToken(Map<String, Object> claims, String subject) {
        final Date createdDate = clock.now();
        final Date expirationDate = calculateExpirationDate(createdDate);
        return Jwts.builder()
                .setClaims(claims)
                .setSubject(subject)
                .setIssuedAt(createdDate)
                .setExpiration(expirationDate)
                .signWith(SignatureAlgorithm.HS512, secret)
                .compact();
    }

    /**
     * @author Tan-Bowen
     * @description TODO 验证token是否过期
     * @date 2021/8/3 11:04
     * @param [token]
     * @paramType [java.lang.String]
     * @return java.lang.Boolean
     */
    private Boolean isTokenExpired(String token) {
        //到期时间
        Date expiration = getExpirationDateFromToken(token);
        return clock.now().before(expiration);
    }
    /**
     * @author Tan-Bowen
     * @description TODO 验证token是否有效
     * @date 2021/8/3 11:03
     * @param [token]
     * @paramType [java.lang.String]
     * @return java.lang.Boolean
     */
    public Boolean validateToken(String token) {
        return isTokenExpired(token);
    }

    private Date calculateExpirationDate(Date createdDate) {
        return new Date(createdDate.getTime() + expiration);
    }
}

